package com.ruoyi.business.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.ruoyi.bizsys.domain.*;
import com.ruoyi.bizsys.dto.UserInfo;
import com.ruoyi.bizsys.enums.AwardEventEnum;
import com.ruoyi.bizsys.enums.EnumFeeType;
import com.ruoyi.bizsys.mapper.*;
import com.ruoyi.bizsys.service.*;
import com.ruoyi.bizsys.utils.BizConstants;
import com.ruoyi.business.common.AsyncService;
import com.ruoyi.business.domain.LoanInfo;
import com.ruoyi.business.domain.LoanRepaymentResult;
import com.ruoyi.business.domain.WalletAccountReq;
import com.ruoyi.business.domain.WithholdTrans;
import com.ruoyi.business.mapper.BCreditAccountMapper;
import com.ruoyi.business.mapper.BLoanInfoMapper;
import com.ruoyi.business.processIou.ProcessForIouService;
import com.ruoyi.business.service.*;
import com.ruoyi.business.util.BathJobMonitorUtil;
import com.ruoyi.common.constant.LoanConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.constant.WalletConstants;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.enums.RspEnmu;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.Func;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.bean.BeanUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.common.utils.uuid.SnowflakeIdWorker;
import com.ruoyi.extdata.service.IExternalApiService;
import com.ruoyi.sms.domain.SmsSendData;
import com.ruoyi.sms.service.ISmsService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.util.*;

@Slf4j
@Service
public class BLoanInfoServiceImpl extends BaseService implements IBLoanInfoService {

    @Autowired
    ISmsService smsService;

    @Autowired
    BLoanInfoMapper loanInfoMapper;

    @Autowired
    LoanIouMapper loanIouMapper;

    @Autowired
    RedissonClient redissonClient;

    @Autowired
    ILoanIouService loanIouService;

    @Autowired
    ILoanIouDeferredService loanIouDeferredService;
    @Autowired
    LoanDeferredOrderMapper loanDeferredOrderMapper;

    @Autowired
    ICreditAccountService creditAccountService;

    @Autowired
    IBatchDateJobService batchDateJobService;

    @Autowired
    ILoanOffsetService loanOffsetService;

    @Autowired
    ICustBankCardService custBankCardService;

    @Autowired
    IDateBatchFailLoanService dateBatchFailLoanService;

    @Autowired
    IRepaymentOrderService repaymentOrderService;

    @Autowired
    IPayTransService payTransService;

    @Autowired
    ILoanOrderService loanOrderService;

    @Autowired
    private RedisCache redisCache;

    @Lazy
    @Autowired
    IRepaymentService repaymentService;

    @Lazy
    @Autowired
    IBRepaymentService iBRepaymentService;

    @Autowired
    IExternalApiService externalApiService;

    @Autowired
    BCreditAccountMapper bCreditAccountMapper;

    @Autowired
    ILoanOverflowService loanOverflowService;

    @Autowired
    BathJobMonitorUtil bathJobMonitorUtil;

    @Autowired
    @Lazy
    IRepaymentSmsService iRepaymentSmsService;

    @Autowired
    private AsyncService asyncService;

    @Autowired
    IAwardDistributionService awardDistributionService;

    @Autowired
    private ProcessForIouService processForIouService;

    @Autowired
    DoCalCustReduce doCalCustReduce;

    @Autowired
    ICustReduceOrderService custReduceOrderService;

    @Autowired
    ReduceConfigMapper reduceConfigMapper;

    @Autowired
    AccountFlowMapper accountFlowMapper;

    @Autowired
    IWalletService walletService;

    @Autowired
    LoanIouDeferredMapper loanIouDeferredMapper;

    @Autowired
    private RestTemplateService restTemplateService;

    public static String theardName = "BatchJob";

    @Override
    public List<LoanInfo> getLoanInfoListByCustNo(String custNo, String loanStatus) {
        return loanInfoMapper.getLoanInfoListByCustNo(custNo, loanStatus);
    }

    @Override
    public LoanInfo getLoanInfoListBySerialNo(String custNo, String serialNo) {
        return loanInfoMapper.getLoanInfoListBySerialNo(custNo, serialNo);
    }

    @Override
    public List<CreditAccount> getBatchJobOnLoanAccount(String batchDate, int startLimit, int step) {
        return loanInfoMapper.getBatchJobOnLoanAccount(batchDate, startLimit, step);
    }

    @Override
    public List<CreditAccount> getBatchJobOnLoanAccount(String batchDate) {
        return loanInfoMapper.getBatchJobOnLoanAccountAll(batchDate);
    }

    /**
     * 查询单个账户信息
     *
     * @param batchDate
     * @param custNo
     * @return
     */
    @Override
    public CreditAccount getOneOnLoanAccountAll(String batchDate, String custNo) {
        return loanInfoMapper.getOneOnLoanAccountAll(batchDate, custNo);
    }

    @Override
    public List<CreditAccount> getLoanOffsetFailAccount() {
        return loanInfoMapper.getLoanOffsetFailAccount();
    }

    @Override
    public int getBatchJobOnLoanAccountNum(String batchDate) {
        Integer result = loanInfoMapper.getBatchJobOnLoanAccountNum(batchDate);
        if (null == result) {
            result = 0;
        }
        return result;
    }

    @Override
    public List<LoanIou> getBatchJobActiveLoanIou(String batchDate, String custNo) {
        return loanInfoMapper.getBatchJobActiveLoanIou(batchDate, custNo);
    }

    @Override
    public void doBatchJobByCust(Date batchDate, CreditAccount account) {
        //锁住客户账户再操作
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + account.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        try {
            long start = System.currentTimeMillis();
            //执行账户跑批
            this.doAccountBatchJob(batchDate, account);
            //无需重试
            long end = System.currentTimeMillis();
            log.info("锁占用时间，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            log.error("执行日批账户号{}更新异常", account.getCustNo(), e);
            // bathJobMonitorUtil.setRedis(theardName, "", e.getMessage(), "doBatchJob线程sleep异常");
            bathJobMonitorUtil.setRedis(theardName, "custNo=" + account.getCustNo(), e.getMessage(), "执行日批更新异常");
        } finally {
            lock.unlock();
        }
    }

    //执行账户跑批
    private void doAccountBatchJob(Date batchDate, CreditAccount account) {
        //重新查询账户信息保持最新
        account = creditAccountService.selectCreditAccountById(account.getId());

        //检查是否有必要跑批
        if (!UserConstants.BUSINESS_NORMAL.equals(account.getState()) //记录状态
                || !UserConstants.BUSINESS_NORMAL.equals(account.getOnLoan()) //在贷状态
                || !batchDate.after(account.getOnLoanActiveDate())) { //在贷激活日期早于跑批日期
            return;
        }

        //根据账户查询所有借据信息
        List<LoanIou> loanIouList = this.getBatchJobActiveLoanIou(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, batchDate), account.getCustNo());
        for (LoanIou loan : loanIouList) {
            DateBatchFailLoan dateBatchFailLoan = new DateBatchFailLoan();
            BeanUtils.copyProperties(loan, dateBatchFailLoan);

            try {
                //还款日
                Date repayDate = DateUtils.parseDate(loan.getRepayDate(), DateUtils.YYYY_MM_DD_SLASH);
                if (null != loan.getIsDeferred() && loan.getIsDeferred() == 1) {
                    // 延期的还款日
                    repayDate = DateUtils.parseDate(loan.getDeferredRepayDate(), DateUtils.YYYY_MM_DD_SLASH);
                }
                //未到还款日
                if (batchDate.before(repayDate)) {
                    //还款日前N天发送短信提醒
                	/*String dateDiffDays = DateUtils.getDateBetween(1, repayDate, batchDate);
                	if(StringUtils.isNotBlank(dateDiffDays)) {
                		dateDiffDays = dateDiffDays.replace("天", "");
                		Double dateDiffDou = Double.parseDouble(dateDiffDays);
                		int dateDiffInt = (int) Math.ceil(dateDiffDou);
                		if(dateDiffInt==3) {
                			sendSmsNotice(creditLine);
                		}
                	}*/
                    continue;
                }

                //判断借据还款日是否大于跑批日-更新借据-并发起代扣
                if (batchDate.after(repayDate)) {
                    //更新借据信息
                    // loan = this.doLoanIouBatchJob(batchDate, loan);
                    // wqg，重写了计算方式
                    //更新状态为逾期
                    loan.setIouStatus(LoanIou.LOAN_IOU_STATUS_OVERDUE);
                    loanIouService.updateLoanIou(loan);
                    if (null != loan.getIsDeferred() && loan.getIsDeferred() == 1) {
                        log.info("更新子借据状态,{}", loan.getLoanSerialNo());
                        // 更新子借据状态
                        LoanIouDeferred loanIouDeferred = new LoanIouDeferred();
                        loanIouDeferred.setStatus(LoanIou.LOAN_IOU_STATUS_OVERDUE);
                        loanIouDeferred.setLoanSerialNo(loan.getLoanSerialNo());
                        loanIouDeferredMapper.updateByloanSerialNo(loanIouDeferred);
                    }
                    loan = processForIouService.init(loan.getLoanSerialNo());
                }

                //判断借据还款日是否等于当天-发起代扣
                //发起代扣申请记录
                //this.createRepaymentOrder(loan, null, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_DATE_BATCH,BigDecimal.ONE);

                log.info("执行日批借据号{}更新成功", loan.getLoanSerialNo());
            } catch (Exception e) {
                bathJobMonitorUtil.setRedis(theardName, "loanSerialNo=" + loan.getLoanSerialNo(), e.getMessage(), "执行日批借据号");
                log.error("执行日批借据号{}更新异常", loan.getLoanSerialNo(), e);

                //借据跑批失败需要记录
                dateBatchFailLoan.setId(null);
                dateBatchFailLoan.setBatchJobDate(batchDate);
                dateBatchFailLoan.setFailState(BizConstants.DATE_BATCH_FAIL_STATUS_FALSE);
                dateBatchFailLoan.setState(UserConstants.BUSINESS_NORMAL);
                dateBatchFailLoan.setRemark(null);
                Date nowTime = new Date();
                dateBatchFailLoan.setCreateTime(nowTime);
                dateBatchFailLoan.setUpdateTime(nowTime);
                dateBatchFailLoanService.insertDateBatchFailLoan(dateBatchFailLoan);
            }
        }

        //更新账户信息
        this.updateAccount(account);
    }

    @Override
    public RepaymentOrder createRepaymentOrder(LoanIou loan, String bankCardAuthorizationCode, String actionType, BigDecimal withholdRate, String batNo) {
        return createRepaymentOrder(loan, bankCardAuthorizationCode, actionType, null, withholdRate, batNo);
    }

    @Override
    public RepaymentOrder createRepaymentOrder(LoanIou loan, String bankCardAuthorizationCode, String actionType, BigDecimal repayAmt, BigDecimal withholdRate, String bathNo) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loan.getCustNo());
        repaymentOrder.setLoanSerialNo(loan.getLoanSerialNo());
        repaymentOrder.setWithholdRate(withholdRate);
        repaymentOrder.setBathNo(bathNo);

        repaymentOrder.setActionType(actionType);

        BigDecimal needTotal = loan.getLoanPrincipal().add(loan.getLoanInterest()).add(loan.getServiceFee()).add(loan.getLateFee());
        BigDecimal repaidTotal = loan.getRepaidPrincipal().add(loan.getRepaidInterest()).add(loan.getRepaidServiceFee()).add(loan.getRepaidLateFee());
        // 计算减免金额
        BigDecimal reduceAmt = loan.getReduceAmt() == null ? BigDecimal.ZERO : loan.getReduceAmt();
        BigDecimal repaymentAmt = needTotal.subtract(repaidTotal).subtract(reduceAmt);

        if (null != repayAmt) {
            repaymentAmt = repayAmt;
        }

        if (repaymentAmt.compareTo(BigDecimal.ZERO) == 1) {//如果金额大于0
            repaymentOrder.setRepaymentAmt(repaymentAmt.setScale(0, BigDecimal.ROUND_UP));
        } else {
            return null;
        }

        repaymentOrder.setReduceFee(reduceAmt);

        repaymentOrder.setLoanPrincipal(loan.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(BigDecimal.ZERO);
        repaymentOrder.setServiceFee(loan.getServiceFee());
        repaymentOrder.setRepaidServiceFee(BigDecimal.ZERO);
        repaymentOrder.setLoanInterest(loan.getLoanInterest());
        repaymentOrder.setRepaidInterest(BigDecimal.ZERO);
        repaymentOrder.setLateFee(loan.getLateFee());
        repaymentOrder.setRepaidLateFee(BigDecimal.ZERO);

        //查询默认还款银行卡
        if (StringUtils.isBlank(bankCardAuthorizationCode)) {
            CustBankCard custBankCard = new CustBankCard();
            custBankCard.setCustNo(loan.getCustNo());
            custBankCard.setIsDefault("Y");
            custBankCard.setState(UserConstants.BUSINESS_NORMAL);
            List<CustBankCard> custBankCardList = custBankCardService.selectCustBankCardList(custBankCard);
            if (custBankCardList.size() < 1) {
                return null;
            }
            bankCardAuthorizationCode = custBankCardList.get(0).getAuthorizationCode();
        }
        repaymentOrder.setBankCard(bankCardAuthorizationCode);
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_WAIT);
        repaymentOrder.setPayTransSerialNo(null);

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForPayNewCard(LoanIou loan, String actionType, String active, String payTransSerialNo, BigDecimal amt, LoanDeferredOrder loanDeferredOrder) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        if (null != loanDeferredOrder) {
            repaymentOrder.setLoanDeferOrderNo(loanDeferredOrder.getLoanDeferOrderNo());
        }
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loan.getCustNo());
        repaymentOrder.setLoanSerialNo(loan.getLoanSerialNo());
        repaymentOrder.setBathNo(active);
        repaymentOrder.setActionType(actionType);
        if (amt.compareTo(BigDecimal.ZERO) == 1) {//如果金额大于0
            repaymentOrder.setRepaymentAmt(amt.setScale(0, BigDecimal.ROUND_UP));
        }
        repaymentOrder.setLoanPrincipal(loan.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(loan.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loan.getServiceFee());
        repaymentOrder.setRepaidServiceFee(loan.getRepaidServiceFee());
        repaymentOrder.setLoanInterest(loan.getLoanInterest());
        repaymentOrder.setRepaidInterest(loan.getRepaidInterest());
        repaymentOrder.setLateFee(loan.getLateFee());
        repaymentOrder.setRepaidLateFee(loan.getRepaidLateFee());
        repaymentOrder.setPayTransSerialNo(payTransSerialNo);
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForWebhook(LoanIou loan, String actionType, String payTransSerialNo, BigDecimal amt, Date payTime, int status) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loan.getCustNo());
        repaymentOrder.setLoanSerialNo(loan.getLoanSerialNo());
        repaymentOrder.setActionType(actionType);
        repaymentOrder.setRepaymentAmt(amt);
        repaymentOrder.setLoanPrincipal(loan.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(loan.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loan.getServiceFee());
        repaymentOrder.setRepaidServiceFee(loan.getRepaidServiceFee());
        repaymentOrder.setLoanInterest(loan.getLoanInterest());
        repaymentOrder.setRepaidInterest(loan.getRepaidInterest());
        repaymentOrder.setLateFee(loan.getLateFee());
        repaymentOrder.setRepaidLateFee(loan.getRepaidLateFee());
        repaymentOrder.setPayTransSerialNo(payTransSerialNo);
        repaymentOrder.setRepayStatus(status);
        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(payTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForWalletPay(BigDecimal paystaskAmt, LoanIou loan, String bankCardAuthorizationCode, String actionType, String bathNo) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loan.getCustNo());
        repaymentOrder.setLoanSerialNo(loan.getLoanSerialNo());
        repaymentOrder.setBathNo(bathNo);

        repaymentOrder.setActionType(actionType);

        if (paystaskAmt.compareTo(BigDecimal.ZERO) == 1) {//如果金额大于0
            repaymentOrder.setRepaymentAmt(paystaskAmt.setScale(0, BigDecimal.ROUND_UP));
        } else {
            return null;
        }


        repaymentOrder.setLoanPrincipal(loan.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(loan.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loan.getServiceFee());
        repaymentOrder.setRepaidServiceFee(loan.getRepaidServiceFee());
        repaymentOrder.setLoanInterest(loan.getLoanInterest());
        repaymentOrder.setRepaidInterest(loan.getRepaidInterest());
        repaymentOrder.setLateFee(loan.getLateFee());
        repaymentOrder.setRepaidLateFee(loan.getRepaidLateFee());

        //查询默认还款银行卡
        if (StringUtils.isBlank(bankCardAuthorizationCode)) {
            CustBankCard custBankCard = new CustBankCard();
            custBankCard.setCustNo(loan.getCustNo());
            custBankCard.setIsDefault("Y");
            custBankCard.setState(UserConstants.BUSINESS_NORMAL);
            List<CustBankCard> custBankCardList = custBankCardService.selectCustBankCardList(custBankCard);
            if (custBankCardList.size() < 1) {
                return null;
            }
            bankCardAuthorizationCode = custBankCardList.get(0).getAuthorizationCode();
        }
        repaymentOrder.setBankCard(bankCardAuthorizationCode);
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_WAIT);
        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForDeferForminPay(BigDecimal paystaskAmt, LoanIou loan, String bankCardAuthorizationCode, String actionType, String bathNo, LoanDeferredOrder loanDeferredOrder) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setLoanDeferOrderNo(loanDeferredOrder.getLoanDeferOrderNo());
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loan.getCustNo());
        repaymentOrder.setLoanSerialNo(loan.getLoanSerialNo());
        repaymentOrder.setBathNo(bathNo);

        repaymentOrder.setActionType(actionType);

        repaymentOrder.setRepaymentAmt(paystaskAmt.setScale(0, BigDecimal.ROUND_UP));

        repaymentOrder.setLoanPrincipal(loan.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(BigDecimal.ZERO);
        repaymentOrder.setServiceFee(loanDeferredOrder.getDeferredServiceFee());
        repaymentOrder.setRepaidServiceFee(BigDecimal.ZERO);
        repaymentOrder.setLoanInterest(loanDeferredOrder.getDeferredInterestFee());
        repaymentOrder.setRepaidInterest(BigDecimal.ZERO);
        repaymentOrder.setLateFee(loan.getLateFee().subtract(loan.getRepaidLateFee()));
        repaymentOrder.setRepaidLateFee(BigDecimal.ZERO);

        //查询默认还款银行卡
        if (StringUtils.isBlank(bankCardAuthorizationCode)) {
            CustBankCard custBankCard = new CustBankCard();
            custBankCard.setCustNo(loan.getCustNo());
            custBankCard.setIsDefault("Y");
            custBankCard.setState(UserConstants.BUSINESS_NORMAL);
            List<CustBankCard> custBankCardList = custBankCardService.selectCustBankCardList(custBankCard);
            if (custBankCardList.size() < 1) {
                return null;
            }
            bankCardAuthorizationCode = custBankCardList.get(0).getAuthorizationCode();
        }
        repaymentOrder.setBankCard(bankCardAuthorizationCode);
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_WAIT);
        repaymentOrder.setPayTransSerialNo(null);

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForDefer(LoanIou loan, String bankCardAuthorizationCode, String actionType, String bathNo, LoanDeferredOrder loanDeferredOrder) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setLoanDeferOrderNo(loanDeferredOrder.getLoanDeferOrderNo());
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loan.getCustNo());
        repaymentOrder.setLoanSerialNo(loan.getLoanSerialNo());
        repaymentOrder.setBathNo(bathNo);

        repaymentOrder.setActionType(actionType);

        repaymentOrder.setRepaymentAmt(loanDeferredOrder.getRepayAmt().setScale(0, BigDecimal.ROUND_UP));

        repaymentOrder.setLoanPrincipal(loan.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(BigDecimal.ZERO);
        repaymentOrder.setServiceFee(loan.getServiceFee());
        repaymentOrder.setRepaidServiceFee(BigDecimal.ZERO);
        repaymentOrder.setLoanInterest(loan.getLoanInterest());
        repaymentOrder.setRepaidInterest(BigDecimal.ZERO);
        repaymentOrder.setLateFee(loan.getLateFee());
        repaymentOrder.setRepaidLateFee(BigDecimal.ZERO);

        //查询默认还款银行卡
        if (StringUtils.isBlank(bankCardAuthorizationCode)) {
            CustBankCard custBankCard = new CustBankCard();
            custBankCard.setCustNo(loan.getCustNo());
            custBankCard.setIsDefault("Y");
            custBankCard.setState(UserConstants.BUSINESS_NORMAL);
            List<CustBankCard> custBankCardList = custBankCardService.selectCustBankCardList(custBankCard);
            if (custBankCardList.size() < 1) {
                return null;
            }
            bankCardAuthorizationCode = custBankCardList.get(0).getAuthorizationCode();
        }
        repaymentOrder.setBankCard(bankCardAuthorizationCode);
        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_WAIT);
        repaymentOrder.setPayTransSerialNo(null);

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForWalletNoSuccess(LoanIou loanIou, String actionType, String active, BigDecimal amtWallet, String walletNo) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loanIou.getCustNo());
        repaymentOrder.setLoanSerialNo(loanIou.getLoanSerialNo());
        repaymentOrder.setActionType(actionType);
        repaymentOrder.setRepaymentAmt(amtWallet);
        repaymentOrder.setPayTransSerialNo(walletNo); // 钱包流水号

        repaymentOrder.setLoanPrincipal(loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()));
        repaymentOrder.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loanIou.getServiceFee());
        repaymentOrder.setRepaidServiceFee(loanIou.getRepaidServiceFee());
        repaymentOrder.setLoanInterest(loanIou.getInterest());
        repaymentOrder.setRepaidInterest(loanIou.getRepaidInterest());
        repaymentOrder.setLateFee(loanIou.getLateFee().subtract(loanIou.getRepaidLateFee()));
        repaymentOrder.setRepaidLateFee(BigDecimal.ZERO);

        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        int isOk = repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return isOk == 1 ? repaymentOrder : null;
    }

    @Override
    public RepaymentOrder createRepaymentOrderRepayAllWalletNoSuccess(LoanIou loanIou, String actionType, String active, BigDecimal amtWallet, String walletOrderNo) {
        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loanIou.getCustNo());
        repaymentOrder.setLoanSerialNo(loanIou.getLoanSerialNo());
        repaymentOrder.setActionType(actionType);
        repaymentOrder.setRepaymentAmt(amtWallet);
        repaymentOrder.setShouldRepaymentAmt(amtWallet);
        repaymentOrder.setPayTransSerialNo(walletOrderNo); // 钱包流水号

        repaymentOrder.setLoanPrincipal(loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()));
        repaymentOrder.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loanIou.getServiceFee());
        repaymentOrder.setRepaidServiceFee(loanIou.getRepaidServiceFee());
        repaymentOrder.setLoanInterest(loanIou.getInterest());
        repaymentOrder.setRepaidInterest(loanIou.getRepaidInterest());
        repaymentOrder.setLateFee(loanIou.getLateFee().subtract(loanIou.getRepaidLateFee()));
        repaymentOrder.setRepaidLateFee(BigDecimal.ZERO);

        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
        repaymentOrder.setPayTime(new Date());

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);

        int isOk = repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return isOk == 1 ? repaymentOrder : null;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForDeferWalletNoSuccess(LoanIou loanIou, String actionType, String active, LoanDeferredOrder loanDeferredOrder, BigDecimal amtWallet, String walletNo) {

        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setLoanDeferOrderNo(loanDeferredOrder.getLoanDeferOrderNo());
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loanIou.getCustNo());
        repaymentOrder.setLoanSerialNo(loanIou.getLoanSerialNo());

        repaymentOrder.setActionType(actionType);

        //BigDecimal needTotal = loanIou.getLoanPrincipal().add(loanIou.getLoanInterest()).add(loanIou.getServiceFee()).add(loanIou.getLateFee());
        //BigDecimal repaidTotal = loanIou.getRepaidPrincipal().add(loanIou.getRepaidInterest()).add(loanIou.getRepaidServiceFee()).add(loanIou.getRepaidLateFee());
        // 计算减免金额
        //BigDecimal reduceAmt = loanIou.getReduceAmt() == null ? BigDecimal.ZERO : loanIou.getReduceAmt();
        BigDecimal repaymentAmt = amtWallet;
        if (repaymentAmt.compareTo(BigDecimal.ZERO) == 1) {//如果金额大于0
            repaymentOrder.setRepaymentAmt(repaymentAmt);
        } else {
            return null;
        }
        repaymentOrder.setPayTransSerialNo(walletNo); // 钱包流水号

        repaymentOrder.setLoanPrincipal(loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal()));
        repaymentOrder.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loanDeferredOrder.getDeferredServiceFee());
        repaymentOrder.setRepaidServiceFee(BigDecimal.ZERO);
        repaymentOrder.setLoanInterest(loanDeferredOrder.getDeferredInterestFee());
        repaymentOrder.setRepaidInterest(BigDecimal.ZERO);
        repaymentOrder.setLateFee(loanIou.getLateFee().subtract(loanIou.getRepaidLateFee()));
        repaymentOrder.setRepaidLateFee(BigDecimal.ZERO);

        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    public RepaymentOrder createRepaymentOrderForDeferWalletSuccess(LoanIou loanIou, String actionType, String active, LoanDeferredOrder loanDeferredOrder, BigDecimal amtWallet, String walletNo) {

        RepaymentOrder repaymentOrder = new RepaymentOrder();
        repaymentOrder.setLoanDeferOrderNo(loanDeferredOrder.getLoanDeferOrderNo());
        repaymentOrder.setRepaymentNo(IdUtils.reqId());
        repaymentOrder.setCustNo(loanIou.getCustNo());
        repaymentOrder.setLoanSerialNo(loanIou.getLoanSerialNo());

        repaymentOrder.setActionType(actionType);

        //BigDecimal needTotal = loanIou.getLoanPrincipal().add(loanIou.getLoanInterest()).add(loanIou.getServiceFee()).add(loanIou.getLateFee());
        //BigDecimal repaidTotal = loanIou.getRepaidPrincipal().add(loanIou.getRepaidInterest()).add(loanIou.getRepaidServiceFee()).add(loanIou.getRepaidLateFee());
        // 计算减免金额
        //BigDecimal reduceAmt = loanIou.getReduceAmt() == null ? BigDecimal.ZERO : loanIou.getReduceAmt();
        BigDecimal repaymentAmt = amtWallet;
        if (repaymentAmt.compareTo(BigDecimal.ZERO) == 1) {//如果金额大于setScale(0, BigDecimal.ROUND_UP));
            repaymentOrder.setRepaymentAmt(repaymentAmt);
        } else {
            return null;
        }
        repaymentOrder.setPayTransSerialNo(walletNo); // 钱包流水号

        repaymentOrder.setLoanPrincipal(loanIou.getLoanPrincipal());
        repaymentOrder.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        repaymentOrder.setServiceFee(loanIou.getServiceFee());
        repaymentOrder.setRepaidServiceFee(loanIou.getRepaidServiceFee());
        repaymentOrder.setLoanInterest(loanIou.getLoanInterest());
        repaymentOrder.setRepaidInterest(loanIou.getRepaidInterest());
        repaymentOrder.setLateFee(loanIou.getLateFee());
        repaymentOrder.setRepaidLateFee(loanIou.getRepaidLateFee());

        repaymentOrder.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);

        repaymentOrder.setState(UserConstants.BUSINESS_NORMAL);
        Date nowTime = new Date();
        repaymentOrder.setPayTime(nowTime);// 当前时间是支付时间
        repaymentOrder.setCreateTime(nowTime);
        repaymentOrder.setUpdateTime(nowTime);
        repaymentOrderService.insertRepaymentOrder(repaymentOrder);
        return repaymentOrder;
    }

    @Override
    @Async("threadPoolTaskExecutor")
    public void sendRepaidResultSms(RepaymentOrder r) {
        try {
            AppUser appUser = getAppUserByCustNo(r.getCustNo());
            if (null != appUser) {

                JSONObject smsParam = new JSONObject();
                String smsType = "";
                if ((BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATE.equals(r.getActionType()) || BizConstants.LOAN_REPAYMENT_ORDER_TYPE_COLLECTION.equals(r.getActionType()))
                        && BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS == r.getRepayStatus()) {

                    LoanOrder loanOrder = new LoanOrder();
                    loanOrder.setLoanSerialNo(r.getLoanSerialNo());
                    List<LoanOrder> LoanOrderList = loanOrderService.selectLoanOrderList(loanOrder);
                    if (LoanOrderList.size() == 1) {
                        loanOrder = LoanOrderList.get(0);
                        smsParam.put("contractNo", loanOrder.getContractNo());
                        String repaymentDate = DateUtils.dateTimeNow(DateUtils.DD_MM_YYYY);
                        smsParam.put("repaymentDate", repaymentDate);
                        smsType = BizConstants.SMS_TYPE_WITHHOLD_SUCCESS;
                    } else {
                        throw new BusinessException(RspEnmu.SYSTEM_EXCEPTION);
                    }
                } else if ((BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATE.equals(r.getActionType()) || BizConstants.LOAN_REPAYMENT_ORDER_TYPE_COLLECTION.equals(r.getActionType()))
                        && BizConstants.REPAYMENT_ORDER_STATUS_FAIL == r.getRepayStatus()) {

                    CustBankCard custBankCard = new CustBankCard();
                    custBankCard.setCustNo(r.getCustNo());
                    custBankCard.setAuthorizationCode(r.getBankCard());
                    custBankCard.setState(UserConstants.BUSINESS_NORMAL);
                    List<CustBankCard> custBankCardList = custBankCardService.selectCustBankCardList(custBankCard);

                    RealNameAuth auth = new RealNameAuth();
                    auth.setUserId(appUser.getUserUuid());
                    auth.setState(BizConstants.BIZ_STATUS_1);
                    List<RealNameAuth> realNameAuthList = realNameAuthService.selectRealNameAuthList(auth);

                    if (custBankCardList.size() == 1 && realNameAuthList.size() == 1) {
                        custBankCard = custBankCardList.get(0);
                        RealNameAuth realNameAuth = realNameAuthList.get(0);

                        smsParam.put("lastName", realNameAuth.getLastName());
                        smsParam.put("bankName", custBankCard.getBankName());
                        String cardNumber = custBankCard.getCardNumber();
                        cardNumber = cardNumber.substring(cardNumber.length() - 4);
                        smsParam.put("cardLast4", cardNumber);
                        smsType = BizConstants.SMS_TYPE_WITHHOLD_FAILED;
                    } else {
                        throw new BusinessException(RspEnmu.SYSTEM_EXCEPTION);
                    }
                }

                if (StringUtils.isNotBlank(smsType)) {
                    smsParam.put("repaymentAmt", String.valueOf(r.getRepaymentAmt()));
                    SmsSendData smsData = SmsSendData.builder().smsType(smsType).noticeType(BizConstants.SMS_NOTICE_TYPE_NOTICE)
                            .countryCode(appUser.getCountryCode()).phoneNumber(appUser.getPhone()).smsParam(smsParam).build();
                    smsService.sendSms(smsData);
                }
            }
        } catch (Exception e) {
            log.error("代扣结果通知短信发送异常", e);
        }
    }

    //执行跑批借据更新
    @Override
    public LoanIou doLoanIouBatchJob(Date batchDate, LoanIou loanIou) throws ParseException {
        //还款日
        //

        //1.查询对应的全部offSet记录，正序排列
        //2.循环offSet记录，找到对应的repaymentOrder记录
        //3.拿到支付时间（如果支付时间小于逾期时间就退出本次），并且模拟冲账拿到剩余本金
        //4.拿到上次支付时间如果没有就拿逾期时间，到支付时间的时间差，并且计算出相加滞纳金额

//        // RepaymentOrder repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        // RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
//        Date repayDate = repaymentOrder!=null&&repaymentOrder.getPayTime()!=null?repaymentOrder.getPayTime():DateUtils.parseDate(loanIou.getRepayDate(), DateUtils.YYYY_MM_DD_SLASH);
//
//
//        //逾期天数计算
//        Instant repayDateInstant = repayDate.toInstant();
//        Instant batchDateInstant = batchDate.toInstant();
//        long daysDiff = Duration.between(repayDateInstant, batchDateInstant).toDays();
//
//        //判断是否在宽限期
//        int lateFeeGracePeriod = loanIou.getLateFeeGracePeriod();
//        if (daysDiff > lateFeeGracePeriod) {
//            daysDiff = daysDiff - lateFeeGracePeriod;
//        }
//
//        //计算滞纳金
//        if (daysDiff > 0) {
//            BigDecimal principalNeed = loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal());//本金还需要还的
//            BigDecimal interestNeed = loanIou.getLoanInterest().subtract(loanIou.getRepaidInterest());//利息还需要还的
//            BigDecimal serviceNeed = loanIou.getServiceFee().subtract(loanIou.getRepaidServiceFee());//还需要还的
//
//            //未还（本金+利息）之和
//            //BigDecimal need = principalNeed.add(interestNeed);//罚息计算基数
//
//            //滞纳金日费率
//            BigDecimal dateLateFee = loanIou.getDateLateFee();
//
//            //费率计算的最大天数
//            if (loanIou.getLateFeeMaxDays() < daysDiff) {
//                daysDiff = loanIou.getLateFeeMaxDays();
//            }
//
//            //计算现需还滞纳金
//            BigDecimal lateFee = principalNeed.multiply(dateLateFee).multiply(new BigDecimal(daysDiff));
//
//            //计算总滞纳金
//            BigDecimal totalLateFee = lateFee.add(loanIou.getRepaidLateFee());
//            loanIou.setLateFee(totalLateFee);
//
//            //计算逾期总金额
//            BigDecimal overdueAmount = principalNeed.add(interestNeed).add(serviceNeed).add(lateFee);
//            loanIou.setOverdueAmount(overdueAmount);
//        }

        //判断当前逾期天数是否达到上限
        Date repayDate = DateUtils.parseDate(loanIou.getRepayDate(), DateUtils.YYYY_MM_DD_SLASH);

        //逾期天数计算
        Instant repayDateInstant = repayDate.toInstant();
        Instant batchDateInstant = batchDate.toInstant();
        long daysDiff = Duration.between(repayDateInstant, batchDateInstant).toDays();

        //判断是否在宽限期
        int lateFeeGracePeriod = loanIou.getLateFeeGracePeriod();
        if (daysDiff > lateFeeGracePeriod) {
            daysDiff = daysDiff - lateFeeGracePeriod;
        }

        if (daysDiff > 0) {
            BigDecimal principalNeed = loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal());//本金还需要还的

            //滞纳金日费率
            BigDecimal dateLateFee = loanIou.getDateLateFee();

            //费率计算的最大天数
            if (daysDiff > loanIou.getLateFeeMaxDays()) {
                return loanIou;
            }
            //逾期金额
            BigDecimal lateFee = principalNeed.multiply(dateLateFee);
            loanIou.setLateFee(loanIou.getLateFee().add(lateFee));

            //逾期总金额
            loanIou.setOverdueAmount(loanIou.getOverdueAmount().add(lateFee));
        }

        //更新状态为逾期
        loanIou.setIouStatus(LoanIou.LOAN_IOU_STATUS_OVERDUE);
        loanIouService.updateLoanIou(loanIou);

        return loanIou;
    }

    //冲账操作
    @Override
    public boolean doLoanRepayment(LoanOffset loanOffset) {
        boolean result = false;
        log.info("客户号{}开始冲账，金额{}", loanOffset.getCustNo(), loanOffset.getAmt());

        if (BizConstants.LOAN_OFFSET_STATUS_FAIL == loanOffset.getLoanOffsetStatus()
                || BizConstants.LOAN_OFFSET_STATUS_WAIT == loanOffset.getLoanOffsetStatus()) {

            //判断日切是否当天
            boolean isTodayBatchJobSuccess = batchDateJobService.isTodayBatchJobSuccess();
            if (!isTodayBatchJobSuccess) {//日切为当天才冲账
                String msg = "日切非当日";
                log.error(msg);
                loanOffset.setLoanOffsetDesc(msg);
                loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
                loanOffsetService.updateLoanOffset(loanOffset);
                return result;
            }
            //锁住客户账户再操作
            String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + loanOffset.getCustNo();
            RLock lock = redisCache.lock(lockKey);
            try {
                long start = System.currentTimeMillis();
                //冲账
                result = this.doLoanRepaymentGetInfo(loanOffset);
                //无需重试
                long end = System.currentTimeMillis();
                log.info("锁占用时间，耗时：" + (end - start) + " ms");
            } catch (Exception e) {
                log.error("冲账线程sleep异常", e);
            } finally {
                lock.unlock();
            }
        }
        return result;
    }

    //冲账
    @Override
    public boolean doLoanRepaymentGetInfo(LoanOffset loanOffset) {
        boolean result = false;
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            result = this.loanOffset(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
//            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
//            loanOffset.setLoanOffsetDesc("异常");
            throw new RuntimeException(e);
        }
        return result;
    }

    //冲账--延期+部分钱包
    @Override
    public void doLoanRepaymentGetInfoForDeferWalletNotAll(LoanOffset loanOffset) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForDeferWalletNotAll(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
//            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
//            loanOffset.setLoanOffsetDesc("异常");
            throw new RuntimeException(e);
        }
    }


    //冲账--非延期+部分钱包
    @Override
    public void doLoanRepaymentGetInfoForWalletNotAll(LoanOffset loanOffset) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForWalletNotAll(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
//            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
//            loanOffset.setLoanOffsetDesc("异常");
            throw new RuntimeException(e);
        }
    }

    /**
     * 钱包延期全额还款
     *
     * @param loanOffset
     */
    @Override
    public void doLoanRepaymentGetInfoForDeferWalletAll(LoanOffset loanOffset) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForDeferWalletAll(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
//            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
//            loanOffset.setLoanOffsetDesc("异常");
            throw new RuntimeException(e);
        }
    }

    @Override
    public void doLoanRepaymentGetInfoForWalletAll(LoanOffset loanOffset) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForWalletAll(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
//            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
//            loanOffset.setLoanOffsetDesc("异常");
            throw new RuntimeException(e);
        }
    }


    //冲账--延期+部分钱包 payStask钱进行冲账
    @Override
    public void doLoanRepaymentGetInfoForDeferWalletNotAllByPayStsk(LoanOffset loanOffset, LoanOffset loanOffsetWallet) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForDeferWalletNotAllPayStask(creditAccount, loanOffset, loanOffsetWallet);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
            throw new RuntimeException(e);
        }
    }


    //冲账--非延期+部分钱包 payStask钱进行冲账
    @Override
    public void doLoanRepaymentGetInfoForWalletNotAllByPayStsk(LoanOffset loanOffset) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForWalletNotAllPayStask(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
            throw new RuntimeException(e);
        }
    }

    private void loanOffsetForWalletNotAllPayStask(CreditAccount creditAccount, LoanOffset loanOffset) {
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        loanOffsetDoSet(loanOffset, loanIou);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoing(creditAccount, loanIou, loanOffset);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
    }

    private void loanOffsetForDeferWalletNotAllPayStask(CreditAccount creditAccount, LoanOffset loanOffset, LoanOffset loanOffsetWallet) {
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        LoanIouDeferred loanIouDeferred = loanIouDeferredMapper.getDetailByDeferNo(loanOffset.getLoanSerialDeferredNo());
        //冲账前借据数据记录--记录子借据的数据
        loanOffsetDoSetForDefer(loanOffset, loanIou, loanIouDeferred);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoingForDeFerWalletNotAllPayStask(creditAccount, loanIou, loanOffset, loanIouDeferred, loanOffsetWallet);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
    }

    private void loanOffsetForWalletAll(CreditAccount creditAccount, LoanOffset loanOffset) {
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        //冲账前借据数据记录-
        loanOffsetDoSetForWalletAll(loanOffset, loanIou);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoing(creditAccount, loanIou, loanOffset);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
    }

    private void loanOffsetForDeferWalletAll(CreditAccount creditAccount, LoanOffset loanOffset) {
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        LoanIouDeferred loanIouDeferred = loanIouDeferredMapper.getDetailByDeferNo(loanOffset.getLoanSerialDeferredNo());
        //冲账前借据数据记录--记录子借据的数据
        loanOffsetDoSetForDefer(loanOffset, loanIou, loanIouDeferred);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoingForDeFer(creditAccount, loanIou, loanOffset, loanIouDeferred);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
    }

    private void loanOffsetForDeferWalletNotAll(CreditAccount creditAccount, LoanOffset loanOffset) {
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        LoanIouDeferred loanIouDeferred = loanIouDeferredMapper.getDetailByDeferNo(loanOffset.getLoanSerialDeferredNo());
        //冲账前借据数据记录--记录子借据的数据
        loanOffsetDoSetForDefer(loanOffset, loanIou, loanIouDeferred);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoingForDeFerWalletNotAll(creditAccount, loanIou, loanOffset, loanIouDeferred);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
    }


    private void loanOffsetForWalletNotAll(CreditAccount creditAccount, LoanOffset loanOffset) {
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        //冲账前借据数据记录--记录子借据的数据
        loanOffsetDoSet(loanOffset, loanIou);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoing(creditAccount, loanIou, loanOffset);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
    }


    //冲账
    @Override
    public void doLoanRepaymentGetInfoForDefer(LoanOffset loanOffset) {
        try {
            //获取账户信息
            CreditAccount creditAccount = creditAccountService.getNormalAcountOnLoan(loanOffset.getCustNo());
            dealCreditAccountNullRes(loanOffset, creditAccount);
            loanOffsetForDefer(creditAccount, loanOffset);
            loanOffsetService.updateLoanOffset(loanOffset);
        } catch (Exception e) {
            log.error("根据冲账记录ID {}，冲账异常", loanOffset.getId(), e);
//            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
//            loanOffset.setLoanOffsetDesc("异常");
            throw new RuntimeException(e);
        }
    }

    /**
     * 处理CreditAccount空异常
     *
     * @param loanOffset
     * @param creditAccount
     */
    private void dealCreditAccountNullRes(LoanOffset loanOffset, CreditAccount creditAccount) {
        if (null == creditAccount) {
            String msg = "根据冲账客户号：" + loanOffset.getCustNo() + " 未匹配到账户信息";
            log.error(msg);
            loanOffset.setLoanOffsetDesc(msg);
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
            throw new RuntimeException("未匹配到账户信息");
        }
    }

    @Override
    public boolean loanOffset(CreditAccount creditAccount, LoanOffset loanOffset) {
        boolean result = false;
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        //冲账前借据数据记录
        loanOffsetDoSet(loanOffset, loanIou);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoing(creditAccount, loanIou, loanOffset);
        if (rst.isSucess()) {
            delQuickRepayReduce(loanOffset);
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
            result = true;
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
        return result;
    }

    /**
     * 处理QuickRepayReduce
     *
     * @param loanOffset
     */
    private void delQuickRepayReduce(LoanOffset loanOffset) {
        if (StringUtils.isNotBlank(loanOffset.getRepaymentNo())) {
            // getRepaymentNo 可能为空，在低于50n下冲账问题
            RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
            if (null != repaymentOrder.getCustReduceOrderId()) {
                // 查询 repayOrder custorderId 是否存在 更新 custoRderid状态
                custReduceOrderService.updateAndAddReduceInfo(repaymentOrder, BizConstants.LOAN_REPAYMENT_ORDER_TYPE_QR_REDUCE);
            }
        }
    }


    public boolean loanOffsetForDefer(CreditAccount creditAccount, LoanOffset loanOffset) {
        boolean result = false;
        //获取借据
        LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(loanOffset.getLoanSerialNo());
        dealIouNUllRes(loanOffset, loanIou);
        LoanIouDeferred loanIouDeferred = loanIouDeferredMapper.getDetailByDeferNo(loanOffset.getLoanSerialDeferredNo());
        //冲账前借据数据记录--记录子借据的数据
        loanOffsetDoSetForDefer(loanOffset, loanIou, loanIouDeferred);
        //开始冲账
        LoanRepaymentResult rst = this.doLoanRepaymentDoingForDeFer(creditAccount, loanIou, loanOffset, loanIouDeferred);
        if (rst.isSucess()) {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_SUCCESS);
            loanOffset.setSurplusAmt(rst.getSurplusAmt());
            result = true;
        } else {
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
        loanOffset.setLoanOffsetDesc(rst.getResultDesc());
        return result;
    }


    private void loanOffsetDoSet(LoanOffset loanOffset, LoanIou loanIou) {
        loanOffset.setLoanPrincipal(loanIou.getLoanPrincipal());
        loanOffset.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        loanOffset.setLoanInterest(loanIou.getLoanInterest());
        loanOffset.setRepaidInterest(loanIou.getRepaidInterest());
        loanOffset.setServiceFee(loanIou.getServiceFee());
        loanOffset.setRepaidServiceFee(loanIou.getRepaidServiceFee());
        loanOffset.setLateFee(loanIou.getLateFee());
        loanOffset.setRepaidLateFee(loanIou.getRepaidLateFee());
        loanOffset.setReduceAmt(loanIou.getFeeRemissionAmt());
    }

    private void loanOffsetDoSetForWalletAll(LoanOffset loanOffset, LoanIou loanIou) {
        loanOffset.setLoanPrincipal(loanIou.getLoanPrincipal());
        loanOffset.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        loanOffset.setLoanInterest(loanIou.getLoanInterest());
        loanOffset.setRepaidInterest(loanIou.getRepaidInterest());
        loanOffset.setServiceFee(loanIou.getServiceFee());
        loanOffset.setRepaidServiceFee(loanIou.getRepaidServiceFee());
        loanOffset.setLateFee(loanIou.getLateFee());
        loanOffset.setRepaidLateFee(loanIou.getRepaidLateFee());
        loanOffset.setReduceAmt(loanIou.getFeeRemissionAmt());
    }

    private void loanOffsetDoSetForDefer(LoanOffset loanOffset, LoanIou loanIou, LoanIouDeferred loanIouDeferred) {

        loanOffset.setLoanPrincipal(loanIou.getLoanPrincipal());
        loanOffset.setRepaidPrincipal(loanIou.getRepaidPrincipal());
        loanOffset.setLoanInterest(loanIouDeferred.getInterestFee());
        loanOffset.setRepaidInterest(loanIouDeferred.getRepaidInterestFee());
        loanOffset.setServiceFee(loanIouDeferred.getServiceFee());
        loanOffset.setRepaidServiceFee(loanIouDeferred.getRepaidServiceFee());
        loanOffset.setLateFee(loanIou.getLateFee());
        loanOffset.setRepaidLateFee(loanIou.getRepaidLateFee());
        loanOffset.setReduceAmt(loanIou.getFeeRemissionAmt());
    }


    private void dealIouNUllRes(LoanOffset loanOffset, LoanIou loanIou) {
        if (null == loanIou) {
            String msg = "根据冲账借据号：" + loanOffset.getLoanSerialNo() + "未匹配到借据信息";
            log.info(msg);
            loanOffset.setLoanOffsetDesc(msg);
            loanOffset.setLoanOffsetStatus(BizConstants.LOAN_OFFSET_STATUS_FAIL);
        }
    }

    @Transactional
    LoanRepaymentResult doLoanRepaymentDoing(CreditAccount creditAccount, LoanIou loanIou, LoanOffset loanOffset) {
        BigDecimal amt = loanOffset.getAmt();
        LoanRepaymentResult rst = new LoanRepaymentResult();
        log.info("客户号{}借据{}开始冲账", creditAccount.getCustNo(), loanIou.getLoanSerialNo());
        //冲账滞纳金
        amt = dealLateFee(amt, checkOffsetPayTime(loanIou, loanOffset), loanOffset, loanIou);
        //冲账利息
        amt = dealInterestFee(loanIou, loanOffset, amt);
        //冲账服务费
        amt = dealServiceFee(loanIou, loanOffset, amt);
        //冲账本金
        amt = dealPrincipal(loanIou, loanOffset, amt);
        //多出来的钱
        rst.setSurplusAmt(amt);
        // RepaymentOrder repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
        if( null == repaymentOrder ){
            // 可能是减免，减免没有repaymentOrder，取getMaxPayTime
            repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        }
        BigDecimal overFlowAmount = BigDecimal.ZERO;
        if (amt.doubleValue() > 0) {
            overFlowAmount = amt;
            dealOverflow(amt, repaymentOrder);
        }
        // 重新计算借据，并更新借据
        calLoanIouAndUpdateIou(loanIou, repaymentOrder);
        //更新账户
        this.updateAccount(creditAccount);
        // 2022/5/20 账目流水
        createAccountFlow(repaymentOrder, loanOffset, overFlowAmount);
        rst.setSucess(true);
        rst.setResultDesc("success");
        return rst;
    }

    private BigDecimal dealInterestFee(LoanIou loanIou, LoanOffset loanOffset, BigDecimal amt) {
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal need = loanIou.getLoanInterest().subtract(loanIou.getRepaidInterest());//还需要还的
            if (need.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal result = amt.subtract(need);//冲账滞纳金后
                if (result.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetInterest(need);
                    loanIou.setRepaidInterest(loanIou.getLoanInterest());
                    amt = result;
                } else {
                    loanOffset.setOffsetInterest(amt);
                    loanIou.setRepaidInterest(loanIou.getRepaidInterest().add(amt));
                    amt = BigDecimal.ZERO;
                }
            }
        }
        return amt;
    }


    private void calDeferLoanIouAndUpdateIou(LoanIou loanIou, RepaymentOrder repaymentOrder,LoanIouDeferred loanIouDeferred,LoanOffset loanOffset) {
        LoanDeferredOrder loanDeferredOrder = loanDeferredOrderMapper.selectByOrderNo(repaymentOrder.getLoanDeferOrderNo());
        loanIou.setDeferredRepayDate(loanIouDeferred.getRepayDate());
        // 这里判断是原来的借据判断
        if (null != loanIou.getIsDeferred() && loanIou.getIsDeferred() == 1) {
            loanIou.setDeferredPeriodNum(loanIou.getDeferredPeriodNum() + loanDeferredOrder.getNumDeferredDay());
        } else {
            loanIou.setDeferredPeriodNum(loanIou.getPeriodNum() + loanDeferredOrder.getNumDeferredDay());
        }
        // 设置延期子借据号到iou
        loanIou.setLoanSerialDeferredNo(loanOffset.getLoanSerialDeferredNo());
        loanIou.setIsDeferred(1);
        // 上面代码，设置延期，事物内，不可以再下一行下面
        calNewIouStatus(loanIou, repaymentOrder);// 计算延期日期，和状态修改

        loanIouService.updateLoanIou(loanIou);
        // 设置延期订单
        loanDeferredOrder.setStatus(LoanDeferredOrder.STATUS_SUCCESS);
        loanDeferredOrder.setLoanSerialDeferredNo(loanOffset.getLoanSerialDeferredNo());
        loanDeferredOrderMapper.updateById(loanDeferredOrder);
        // 更新子借据状态和iou一致
        loanIouDeferred.setStatus(loanIou.getIouStatus());
        loanIouDeferredMapper.updateById(loanIouDeferred);
    }


    private void calLoanIouAndUpdateIou(LoanIou loanIou, RepaymentOrder repaymentOrder) {
        calNewIouStatus(loanIou, repaymentOrder);
        loanIouService.updateLoanIou(loanIou);
        loanIouDeferredService.updateByloanSerialNo(loanIou);
    }

    private BigDecimal dealPrincipal(LoanIou loanIou, LoanOffset loanOffset, BigDecimal amt) {
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal need = loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal());//还需要还的
            if (need.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal result = amt.subtract(need);//冲账滞纳金后
                if (result.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetPrincipal(need);
                    loanIou.setRepaidPrincipal(loanIou.getLoanPrincipal());
                    amt = result;
                } else {
                    loanOffset.setOffsetPrincipal(amt);
                    loanIou.setRepaidPrincipal(loanIou.getRepaidPrincipal().add(amt));
                    amt = BigDecimal.ZERO;
                }
            }
        }
        return amt;
    }

    private BigDecimal dealServiceFee(LoanIou loanIou, LoanOffset loanOffset, BigDecimal amt) {
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal serviceFeeNeed = loanIou.getServiceFee().subtract(loanIou.getRepaidServiceFee());//还需要还的
            if (serviceFeeNeed.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal serviceFeeResult = amt.subtract(serviceFeeNeed);//冲账滞纳金后
                if (serviceFeeResult.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetServiceFee(serviceFeeNeed);
                    loanIou.setRepaidServiceFee(loanIou.getServiceFee());
                    amt = serviceFeeResult;
                } else {
                    loanOffset.setOffsetServiceFee(amt);
                    loanIou.setRepaidServiceFee(loanIou.getRepaidServiceFee().add(amt));
                    amt = BigDecimal.ZERO;
                }
            }
        }
        return amt;
    }

    private BigDecimal dealLateFee(BigDecimal amt, BigDecimal needAmt, LoanOffset loanOffset, LoanIou loanIou) {
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal lateFeeNeed = needAmt;//还需要还的
            //BigDecimal lateFeeNeed = checkOffsetPayTime(loanIou,loanOffset);
            if (lateFeeNeed.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal lateFeeResult = amt.subtract(lateFeeNeed);//冲账滞纳金后
                if (lateFeeResult.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetLateFee(lateFeeNeed);
                    loanIou.setRepaidLateFee(loanIou.getLateFee().subtract(loanIou.getFeeRemissionAmt()));
                    amt = lateFeeResult;
                } else {
                    loanOffset.setOffsetLateFee(amt);
                    loanIou.setRepaidLateFee(loanIou.getRepaidLateFee().add(amt));
                    amt = BigDecimal.ZERO;
                }
            }
        }
        return amt;
    }


    /**
     * 延期包的冲账+部分钱包paystask的还款
     * 先查询钱包的冲账
     * @param creditAccount
     * @param loanIou
     * @param loanOffset
     * @return
     */
    @Transactional
    LoanRepaymentResult doLoanRepaymentDoingForDeFerWalletNotAllPayStask(CreditAccount creditAccount,
                                                                         LoanIou loanIou, LoanOffset loanOffset, LoanIouDeferred loanIouDeferred, LoanOffset loanOffsetWallet) {
        BigDecimal amt = loanOffset.getAmt();
        LoanRepaymentResult rst = new LoanRepaymentResult();
        log.info("客户号{}借据{}开始冲账", creditAccount.getCustNo(), loanIou.getLoanSerialNo());
        //冲账滞纳金
        amt = dealLateFee(amt, checkOffsetPayTime(loanIou, loanOffset), loanOffset, loanIou);
        //冲账利息
        amt = dealInterestFee(loanIou, loanOffset, amt);
        //冲账服务费
        amt = dealServiceFee(loanIou, loanOffset, amt);
        // 冲账延期的利息
        amt = dealDeferredInterestFee(loanOffset, loanIouDeferred, amt);
        // 冲账延期的服务费
        amt = dealDeferredServiceFee(loanOffset, loanIouDeferred, amt);
        //冲账本金
        amt = dealPrincipal(loanIou, loanOffset, amt);
        //多出来的钱
        rst.setSurplusAmt(amt);
        RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
        if( null == repaymentOrder ){
            // 可能是减免，减免没有repaymentOrder，取getMaxPayTime
            repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        }
        BigDecimal overFlowAmount = BigDecimal.ZERO;
        if (amt.doubleValue() > 0) {
            overFlowAmount = amt;
            dealOverflow(amt, repaymentOrder);
        }
        calDeferLoanIouAndUpdateIou(loanIou, repaymentOrder,loanIouDeferred,loanOffset);
        // 更新钱包的RepaymentOrder
        RepaymentOrder repaymentOrderWallet = repaymentOrderService.getDetail(loanOffsetWallet.getRepaymentNo());
        repaymentOrderWallet.setPayTime(new Date());
        repaymentOrderWallet.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
        repaymentOrderService.updateRepaymentOrder(repaymentOrderWallet);
        //更新账户
        this.updateAccount(creditAccount);
        // 2022/5/20 账目流水
        createAccountFlow(repaymentOrder, loanOffset, overFlowAmount);
        rst.setSucess(true);
        rst.setResultDesc("success");
        //结清后自动化提额
        if (LoanIou.LOAN_IOU_STATUS_SETTLE == loanIou.getIouStatus()) {
            try {
//                RuleIn ruleIn = raiseLimitParamsLoader.initRuleInParam(loanIou.getLoanSerialNo(), creditAccount.getCustNo());
                RuleIn ruleIn = restTemplateService.initRuleInParam(loanIou.getLoanSerialNo(), creditAccount.getCustNo());
                asyncService.automaticRaiseLimit(loanIou.getLoanSerialNo(), loanIou.getCustNo(), ruleIn);

                //分销活动
                UserInfo userInfo = appUserService.getUserInfoByCustNo(loanIou.getCustNo());
                awardDistributionService.distributionAwardTrigger(AwardEventEnum.WITHDRAW_SETTLE, userInfo.getUserId(), loanIou.getRepaidPrincipal());

            } catch (Exception e) {
                log.error("客户号{}借据{}自动化提额异常", loanIou.getCustNo(), loanIou.getLoanSerialNo(), e);
            }
        }

        return rst;
    }

    /**
     * 延期包的冲账+部分钱包的还款
     * 先查询钱包的冲账
     * @param creditAccount
     * @param loanIou
     * @param loanOffset
     * @return
     */
    @Transactional
    LoanRepaymentResult doLoanRepaymentDoingForDeFerWalletNotAll(CreditAccount creditAccount,
                                                                 LoanIou loanIou, LoanOffset loanOffset, LoanIouDeferred loanIouDeferred) {
        BigDecimal amt = loanOffset.getAmt();
        LoanRepaymentResult rst = new LoanRepaymentResult();
        log.info("客户号{}借据{}开始冲账", creditAccount.getCustNo(), loanIou.getLoanSerialNo());
        //冲账滞纳金
        amt = dealLateFee(amt, checkOffsetPayTime(loanIou, loanOffset), loanOffset, loanIou);
        //冲账利息
        amt = dealInterestFee(loanIou, loanOffset, amt);
        //冲账服务费
        amt = dealServiceFee(loanIou, loanOffset, amt);
        // 冲账延期的利息
        amt = dealDeferredInterestFee(loanOffset, loanIouDeferred, amt);
        // 冲账延期的服务费
        amt = dealDeferredServiceFee(loanOffset, loanIouDeferred, amt);
        //冲账本金
        amt = dealPrincipal(loanIou, loanOffset, amt);
        //多出来的钱
        rst.setSurplusAmt(amt);
        // RepaymentOrder repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
        if( null == repaymentOrder ){
            // 可能是减免，减免没有repaymentOrder，取getMaxPayTime
            repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        }
        BigDecimal overFlowAmount = BigDecimal.ZERO;
        if (amt.doubleValue() > 0) {
            overFlowAmount = amt;
            dealOverflow(amt, repaymentOrder);
        }
        //更新借据
        calNewIouStatus(loanIou, repaymentOrder);
        // 钱包还款这部分数据先更新，但不进行状态修改
        loanIouService.updateLoanIou(loanIou);
        loanIouDeferredMapper.updateById(loanIouDeferred);
        //更新账户
        this.updateAccount(creditAccount);
        // 2022/5/20 账目流水
        createAccountFlow(repaymentOrder, loanOffset, overFlowAmount);

        rst.setSucess(true);
        rst.setResultDesc("success");

        return rst;
    }

    private void calNewIouStatus(LoanIou loanIou, RepaymentOrder repaymentOrder) {
        BigDecimal leftAmt = LoanIou.calculationOverdueAmount(loanIou);
        if (leftAmt.compareTo(BigDecimal.ZERO) > 0 ) {
            //未结清
            //判断当前时间和还款日的关系
            Date repayDate = DateUtils.dateTime(DateUtils.YYYY_MM_DD_SLASH, loanIou.getRepayDate());
            if(null!= loanIou.getIsDeferred() && loanIou.getIsDeferred()==1){
                // 是延期要获取延期的时间
                repayDate = DateUtils.dateTime(DateUtils.YYYY_MM_DD_SLASH, loanIou.getDeferredRepayDate());
            }
            Calendar calendar = new GregorianCalendar();
            calendar.setTime(repayDate);
            calendar.add(calendar.DATE, 1); //把日期往后增加一天
            repayDate = calendar.getTime();
            if (repayDate.before(new Date())) {//逾期
                loanIou.setOverdueAmount(leftAmt);
                loanIou.setIouStatus(LoanIou.LOAN_IOU_STATUS_OVERDUE);
            } else {
                loanIou.setIouStatus(LoanIou.LOAN_IOU_STATUS_NORMAL);
            }
        } else {//结清
            loanIou.setOverdueAmount(BigDecimal.ZERO);
            loanIou.setIouStatus(LoanIou.LOAN_IOU_STATUS_SETTLE);
            //找到repyamentOrder里时间最大的payTime
            loanIou.setSettleDate(repaymentOrder.getPayTime());
        }
    }

    @Override
    public void doOverFlowAndWallet(BigDecimal amt, RepaymentOrder repaymentOrder) {
        dealOverflow(amt, repaymentOrder);
    }

    private void dealOverflow(BigDecimal amt, RepaymentOrder repaymentOrder) {
        log.info("更新客户溢缴款：amt = {}, repaymentOrder = {}", amt, repaymentOrder.toString());
        addOverflowIncomeAndExpense(amt, repaymentOrder);
        walletService.updateWalletAccount(getWalletAccountReq(amt, repaymentOrder));
    }

    private WalletAccountReq getWalletAccountReq(BigDecimal amt, RepaymentOrder repaymentOrder) {
        return WalletAccountReq.builder()
                .amount(amt)
                .custNo(repaymentOrder.getCustNo())
                .payTime(repaymentOrder.getPayTime())
                .reference(repaymentOrder.getPayTransSerialNo())
                .fundsType(WalletConstants.WALLET_FLOW_FUNDS_TYPE_OVERFLOW)
                .build();
    }

    private void addOverflowIncomeAndExpense(BigDecimal amt, RepaymentOrder repaymentOrder) {
        String type = getOverflowType(repaymentOrder.getActionType());
        saveOverflow(buildOverflowIncome(amt, repaymentOrder, type));
        saveOverflow(buildOverflowExpense(amt, repaymentOrder));
    }

    private void saveOverflow(LoanOverflow overflow) {
        loanOverflowService.insertLoanOverflow(overflow);
    }

    private LoanOverflow buildOverflowExpense(BigDecimal amt, RepaymentOrder repaymentOrder) {
        LoanOverflow loanOverflow = new LoanOverflow();
        loanOverflow.setChangeAmount(amt.negate());
        loanOverflow.setChangeType(BizConstants.OVERFLOW_CHANGE_TYPE_EXPENSE);
        loanOverflow.setCustNo(repaymentOrder.getCustNo());
        loanOverflow.setType(WalletConstants.OVERFLOW_TO_WALLET);
        loanOverflow.setRepaymentOrderNo(repaymentOrder.getRepaymentNo());
        loanOverflow.setLoanIouNo(repaymentOrder.getLoanSerialNo());
        loanOverflow.setTransactionReference(repaymentOrder.getPayTransSerialNo());
        return loanOverflow;
    }

    private LoanOverflow buildOverflowIncome(BigDecimal amt, RepaymentOrder repaymentOrder, String type) {
        LoanOverflow loanOverflow = new LoanOverflow();
        loanOverflow.setChangeAmount(amt);
        loanOverflow.setChangeType(BizConstants.OVERFLOW_CHANGE_TYPE_INCOME);
        loanOverflow.setCustNo(repaymentOrder.getCustNo());
        loanOverflow.setOverflowTime(repaymentOrder.getPayTime());
        loanOverflow.setType(type);
        loanOverflow.setRepaymentOrderNo(repaymentOrder.getRepaymentNo());
        loanOverflow.setLoanIouNo(repaymentOrder.getLoanSerialNo());
        loanOverflow.setTransactionReference(repaymentOrder.getPayTransSerialNo());
        return loanOverflow;
    }

    private String getOverflowType(String actionType) {
        log.info("getOverflowType:" + actionType);
        if (actionType.equals("Monnify-Transfer")) {
            return BizConstants.OVERFLOW_TYPE_OVERFLOW_TRANSFERTOUS;
        }
        if (actionType.toLowerCase().contains("paystack")) {
            return BizConstants.OVERFLOW_TYPE_OVERFLOW_WITHHOLD;
        }
        if (actionType.toLowerCase().contains("wallet")) {
            return BizConstants.OVERFLOW_TYPE_OVERFLOW_WALLET;
        }
        return actionType;
    }

    /**
     * 延期包的冲账
     *
     * @param creditAccount
     * @param loanIou
     * @param loanOffset
     * @return
     */
    @Transactional
    LoanRepaymentResult doLoanRepaymentDoingForDeFer(CreditAccount creditAccount, LoanIou loanIou, LoanOffset loanOffset, LoanIouDeferred loanIouDeferred) {
        BigDecimal amt = loanOffset.getAmt();
        LoanRepaymentResult rst = new LoanRepaymentResult();
        log.info("客户号{}借据{}开始冲账{}", creditAccount.getCustNo(), loanIou.getLoanSerialNo(), loanIouDeferred.toString());
        //冲账滞纳金
        amt = dealLateFee(amt, checkOffsetPayTime(loanIou, loanOffset), loanOffset, loanIou);
        //冲账利息
        amt = dealInterestFee(loanIou, loanOffset, amt);
        //冲账服务费
        amt = dealServiceFee(loanIou, loanOffset, amt);
        // 冲账延期的利息
        amt = dealDeferredInterestFee(loanOffset, loanIouDeferred, amt);
        // 冲账延期的服务费
        amt = dealDeferredServiceFee(loanOffset, loanIouDeferred, amt);
        //冲账本金
        amt = dealPrincipal(loanIou, loanOffset, amt);
        //多出来的钱
        rst.setSurplusAmt(amt);
        // RepaymentOrder repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
        if( null == repaymentOrder ){
            // 可能是减免，减免没有repaymentOrder，取getMaxPayTime
            repaymentOrder = iBRepaymentService.getMaxPayTime(loanIou.getLoanSerialNo());
        }
        BigDecimal overFlowAmount = BigDecimal.ZERO;
        if (amt.doubleValue() > 0) {
            overFlowAmount = amt;
            dealOverflow(amt, repaymentOrder);
        }
        //更新借据
        calDeferLoanIouAndUpdateIou(loanIou, repaymentOrder,loanIouDeferred,loanOffset);
        //更新账户
        this.updateAccount(creditAccount);
        // 2022/5/20 账目流水
        createAccountFlow(repaymentOrder, loanOffset, overFlowAmount);
        rst.setSucess(true);
        rst.setResultDesc("success");
        return rst;
    }

    private BigDecimal dealDeferredServiceFee(LoanOffset loanOffset, LoanIouDeferred loanIouDeferred, BigDecimal amt) {
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal serviceFeeNeed = loanIouDeferred.getServiceFee().subtract(loanIouDeferred.getRepaidServiceFee());//还需要还的
            if (serviceFeeNeed.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal serviceFeeResult = amt.subtract(serviceFeeNeed);//冲账滞纳金后
                if (serviceFeeResult.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetDeferredServiceFee(serviceFeeNeed);
                    loanIouDeferred.setRepaidServiceFee(loanIouDeferred.getServiceFee());
                    amt = serviceFeeResult;
                } else {
                    loanOffset.setOffsetDeferredServiceFee(amt);
                    loanIouDeferred.setRepaidServiceFee(loanIouDeferred.getRepaidServiceFee().add(amt));
                    amt = BigDecimal.ZERO;
                }
            }
        }
        return amt;
    }

    private BigDecimal dealDeferredInterestFee(LoanOffset loanOffset, LoanIouDeferred loanIouDeferred, BigDecimal amt) {
        if (amt.compareTo(BigDecimal.ZERO) > 0) {//如果金额大于0
            BigDecimal need = loanIouDeferred.getInterestFee().subtract(loanIouDeferred.getRepaidInterestFee());//还需要还的
            if (need.compareTo(BigDecimal.ZERO) > 0) {//还需要还
                BigDecimal result = amt.subtract(need);//冲账后
                if (result.compareTo(BigDecimal.ZERO) > 0) {//冲账后》0
                    loanOffset.setOffsetDeferredInterestFee(need);
                    loanIouDeferred.setRepaidInterestFee(loanIouDeferred.getInterestFee());
                    amt = result;
                } else {
                    loanOffset.setOffsetDeferredInterestFee(amt);
                    loanIouDeferred.setRepaidInterestFee(loanIouDeferred.getRepaidInterestFee().add(amt));
                    amt = BigDecimal.ZERO;
                }
            }
        }
        return amt;
    }

    /**
     * 计算滞纳金 ，系统进行减免
     *
     * @param loanOffset
     * @return
     */
    private BigDecimal checkOffsetPayTime(LoanIou loanIou, LoanOffset loanOffset) {
        if (StringUtils.equalsAny(loanOffset.getActionType(),BizConstants.LOAN_REPAYMENT_ORDER_TYPE_SYSTEM_REDUCE
                ,BizConstants.LOAN_REPAYMENT_ORDER_TYPE_COLLECION_REDUCE)) {
            return loanIou.getLateFee().subtract(loanIou.getRepaidLateFee()).subtract(loanIou.getFeeRemissionAmt());
        }
        //还需要还的
        BigDecimal nowPrincipal = loanIou.getLoanPrincipal().subtract(loanIou.getRepaidPrincipal());
        BigDecimal lateFeeNeed = loanIou.getLateFee().subtract(loanIou.getRepaidLateFee()).subtract(loanIou.getFeeRemissionAmt());
        Date repayDate = DateUtils.parseDate(loanIou.getRepayDate());
        if (null != loanIou.getIsDeferred() && loanIou.getIsDeferred() == 1) {
            log.info("存在有效的延期包 判断系统减免时间：{}宽限日{}", loanIou.getDeferredRepayDate(), loanIou.getLateFeeGracePeriod());
            repayDate = DateUtils.parseDate(loanIou.getDeferredRepayDate());
        }
        log.info("repayDate：{} 宽限日{}", repayDate, loanIou.getLateFeeGracePeriod());
        if (DateUtils.daysBetween(repayDate, new Date()) > 0) {
            //已经逾期，计算payTime和还款日的天数
            Date payTime = loanOffset.getPayTime();
            if (null == payTime) {
                RepaymentOrder repaymentOrder = repaymentOrderService.getDetail(loanOffset.getRepaymentNo());
                loanOffset.setPayTime(repaymentOrder.getPayTime());
                payTime = repaymentOrder.getPayTime();
            }
            int LateFeeGracePeriod = loanIou.getLateFeeGracePeriod() == null ? 0 : loanIou.getLateFeeGracePeriod();
            String lastLateFeeDay = DateUtils.getNewDay(DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_SLASH, repayDate), LateFeeGracePeriod);

            if (null != loanIou.getIsDeferred() && loanIou.getIsDeferred() == 1) {
                // 是延期后 最后的时间就是延期的还款日
                lastLateFeeDay = loanIou.getDeferredRepayDate();
            }
            // 支付时间到卡宽限日的天数
            int isLeftfreeDays = DateUtils.daysBetween(payTime, DateUtils.parseDate(lastLateFeeDay));
            // 今天到宽限日的天数
            // >= 0 // 宽限日当天和之前支付的，计算今天-宽限日的天数
            int leftDays = DateUtils.daysBetween(DateUtils.parseDate(lastLateFeeDay), new Date());
            if (isLeftfreeDays < 0) {
                // 在宽限日后支付的 计算今天-宽限日的天数+isLeftfreeDays（负的）
                leftDays = leftDays + isLeftfreeDays;
            }
            log.info("罚息计算减免：lastLateFeeDay:{},isLeftfreeDays:{} ,leftDays:{}", lastLateFeeDay, isLeftfreeDays, leftDays);
            if (leftDays > 0) {
                //不是今天的，需要减免 延迟还款导致购买延期包的问题
                BigDecimal oldFeeRemissionAmt = loanIou.getFeeRemissionAmt();
                BigDecimal feeRemissionAmt = nowPrincipal.multiply(loanIou.getDateLateFee()).multiply(BigDecimal.valueOf(leftDays));
                // feeRemissionAmt = feeRemissionAmt.add(lateFeeNeed);
                // feeRemissionAmt加上lateFeeNeed 原来的需要的还的，期间可能其他还款还掉了罚息的问题
                // 锁的问题，数据库加
                loanIou.setFeeRemissionAmt(feeRemissionAmt);
                loanIouMapper.updateLateFee(loanIou);
                loanIou.setFeeRemissionAmt(oldFeeRemissionAmt.add(feeRemissionAmt));
                log.info("payTime:{},today:{},需要系统减免{}天,{}"
                        , DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_SLASH, payTime)
                        , DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_SLASH, new Date())
                        , leftDays, feeRemissionAmt);
                return lateFeeNeed.subtract(feeRemissionAmt);
            }
        }

        return lateFeeNeed;
    }

    /**
     * 记录账目流水
     * 减免的，判断减免是哪一项才需要单个记录
     *
     * @param repaymentOrder
     * @param loanOffset
     */
    private void createAccountFlow(RepaymentOrder repaymentOrder, LoanOffset loanOffset, BigDecimal overFlowAmount) {
        AccountFlow accountFlow = new AccountFlow();
        accountFlow.setCustNo(repaymentOrder.getCustNo());
        accountFlow.setRelativeSerialNo(repaymentOrder.getLoanSerialNo());
        accountFlow.setReference(repaymentOrder.getPayTransSerialNo());
        accountFlow.setTransferChannel(loanOffset.getActionType());
        accountFlow.setTransferTime(repaymentOrder.getCreateTime());
        accountFlow.setOffsetTime(loanOffset.getCreateTime());
        accountFlow.setDataSource("repayment_order、loan_offset");
        accountFlow.setState(BizConstants.BIZ_STATUS_1);

        insertAccountFlowPrincipal(loanOffset, accountFlow);
        insertAccountFlowInterest(loanOffset, accountFlow);
        insertAccountFLowLateFee(loanOffset, accountFlow);
        insertAccountFlowServiceFee(loanOffset, accountFlow);
        insertAccountFlowOverFlow(overFlowAmount, accountFlow);
        insertAccountFlowDefer(loanOffset, accountFlow);
    }

    /**
     * 延期服务费+利息
     *
     * @param loanOffset
     * @param accountFlow
     */
    private void insertAccountFlowDefer(LoanOffset loanOffset, AccountFlow accountFlow) {
        // 判断入账--服务费
        if (null != loanOffset.getOffsetDeferredServiceFee() && loanOffset.getOffsetDeferredServiceFee().compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(loanOffset.getOffsetDeferredServiceFee());
            accountFlow.setFeeType(EnumFeeType.REPAID_DEFER_SERVICE_FEE.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
        // 延期利息
        if (null != loanOffset.getOffsetDeferredInterestFee() && loanOffset.getOffsetDeferredInterestFee().compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(loanOffset.getOffsetDeferredInterestFee());
            accountFlow.setFeeType(EnumFeeType.REPAID_DEFER_INTEREST.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
    }

    /**
     * 账目流水--溢缴款
     *
     * @param overFlowAmount
     * @param accountFlow
     */
    private void insertAccountFlowOverFlow(BigDecimal overFlowAmount, AccountFlow accountFlow) {
        // 判断入账--溢缴款
        if (overFlowAmount.compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(overFlowAmount);
            accountFlow.setFeeType(EnumFeeType.OVERPAY_IN.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
    }


    private void insertAccountFlowServiceFee(LoanOffset loanOffset, AccountFlow accountFlow) {
        // 判断入账--减服务费
        BigDecimal OffsetServiceFee = loanOffset.getOffsetServiceFee() == null ? BigDecimal.ZERO : loanOffset.getOffsetServiceFee();
        if (OffsetServiceFee.compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(loanOffset.getOffsetServiceFee());
            accountFlow.setFeeType(EnumFeeType.REPAID_SERVICE_FEE.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
    }

    /**
     * 账目流水--滞纳金
     *
     * @param loanOffset
     * @param accountFlow
     */
    private void insertAccountFLowLateFee(LoanOffset loanOffset, AccountFlow accountFlow) {
        // 判断入账--还款罚息
        BigDecimal OffsetLateFee = loanOffset.getOffsetLateFee() == null ? BigDecimal.ZERO : loanOffset.getOffsetLateFee();
        if (OffsetLateFee.compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(loanOffset.getOffsetLateFee());
            accountFlow.setFeeType(EnumFeeType.REPAID_LATE_FEE.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
    }

    /**
     * 记录账目流水-还款利息
     *
     * @param loanOffset
     * @param accountFlow
     */
    private void insertAccountFlowInterest(LoanOffset loanOffset, AccountFlow accountFlow) {
        // 判断入账--还款利息
        BigDecimal OffsetInterest = loanOffset.getOffsetInterest() == null ? BigDecimal.ZERO : loanOffset.getOffsetInterest();
        if (OffsetInterest.compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(loanOffset.getOffsetInterest());
            accountFlow.setFeeType(EnumFeeType.REPAID_INTEREST.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
    }

    /**
     * 记录账目流水-还款本金
     *
     * @param loanOffset
     * @param accountFlow
     */
    private void insertAccountFlowPrincipal(LoanOffset loanOffset, AccountFlow accountFlow) {
        // 判断入账--还款本金
        BigDecimal offSetPrincipal = loanOffset.getOffsetPrincipal() == null ? BigDecimal.ZERO : loanOffset.getOffsetPrincipal();
        if (offSetPrincipal.compareTo(BigDecimal.ZERO) > 0) {
            accountFlow.setAmt(loanOffset.getOffsetPrincipal());
            accountFlow.setFeeType(EnumFeeType.REPAID_PRINCIPAL.getCode());
            accountFlow.setId(IdWorker.getId());
            accountFlowMapper.insertByIndex(accountFlow);
        }
    }


    //更新账户
    @Override
    public void updateAccount(CreditAccount creditAccount) {
        //获取账户之前的状态
        String oldOnLoanStr = creditAccount.getOnLoan();

        //获取所有未结清的借据
        List<LoanIou> activeLoanList = loanInfoMapper.getActiveLoanIouByCustNo(creditAccount.getCustNo());
        if (null != activeLoanList && activeLoanList.size() < 1) {
            creditAccount.setOnLoan(BizConstants.ACCOUNT_ON_lOAN_STATUS_0);
            creditAccount.setOnLoanActiveDate(null);
        } else {
            if (UserConstants.BUSINESS_NOT_NORMAL.equals(oldOnLoanStr)) {
                creditAccount.setOnLoan(BizConstants.ACCOUNT_ON_lOAN_STATUS_1);
                //设置账户onloan的激活日期
                Calendar calendar = Calendar.getInstance();
                calendar.setTime(new Date());
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.set(Calendar.MINUTE, 0);
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MILLISECOND, 0);
                creditAccount.setOnLoanActiveDate(calendar.getTime());
            }
        }

        //逾期标志
        String isOverdue = "0";
        BigDecimal loanPrincipal = BigDecimal.ZERO;
        BigDecimal repaidPrincipal = BigDecimal.ZERO;
        BigDecimal loanInterest = BigDecimal.ZERO;
        BigDecimal repaidInterest = BigDecimal.ZERO;
        BigDecimal serviceFee = BigDecimal.ZERO;
        BigDecimal repaidServiceFee = BigDecimal.ZERO;
        BigDecimal lateFee = BigDecimal.ZERO;
        BigDecimal repaidLateFee = BigDecimal.ZERO;
        BigDecimal overdueAmount = BigDecimal.ZERO;
        BigDecimal loanQuota = BigDecimal.ZERO;

        for (LoanIou activeLoan : activeLoanList) {
            loanPrincipal = loanPrincipal.add(activeLoan.getLoanPrincipal());
            repaidPrincipal = repaidPrincipal.add(activeLoan.getRepaidPrincipal());
            loanInterest = loanInterest.add(activeLoan.getLoanInterest());
            repaidInterest = repaidInterest.add(activeLoan.getRepaidInterest());
            serviceFee = serviceFee.add(activeLoan.getServiceFee());
            repaidServiceFee = repaidServiceFee.add(activeLoan.getRepaidServiceFee());
            lateFee = lateFee.add(activeLoan.getLateFee());
            repaidLateFee = repaidLateFee.add(activeLoan.getRepaidLateFee());
            overdueAmount = overdueAmount.add(activeLoan.getOverdueAmount());
            if (LoanIou.LOAN_IOU_STATUS_OVERDUE == activeLoan.getIouStatus()) {
                isOverdue = "1";
            }
            //未还金额
            BigDecimal need = activeLoan.getLoanPrincipal().subtract(activeLoan.getRepaidPrincipal());
            loanQuota = loanQuota.add(need);
        }
        creditAccount.setLoanPrincipal(loanPrincipal);
        creditAccount.setRepaidPrincipal(repaidPrincipal);
        creditAccount.setLoanInterest(loanInterest);
        creditAccount.setRepaidInterest(repaidInterest);
        creditAccount.setServiceFee(serviceFee);
        creditAccount.setRepaidServiceFee(repaidServiceFee);
        creditAccount.setLateFee(lateFee);
        creditAccount.setRepaidLateFee(repaidLateFee);
        creditAccount.setOverdueAmount(overdueAmount);
        if (UserConstants.BUSINESS_NORMAL.equals(isOverdue)) {
            creditAccount.setIsOverdue(UserConstants.BUSINESS_NORMAL);
        } else {
            creditAccount.setIsOverdue(UserConstants.BUSINESS_NOT_NORMAL);
        }
        //更新额度
        creditAccount.setLoanQuota(loanQuota);
        bCreditAccountMapper.updateCreditAccount(creditAccount);
    }

    /**
     * 代扣处理更新代扣交易流水号
     *
     * @param r
     * @param email * @return
     */
    public Map<String, Boolean> payTransWithholdTrans(RepaymentOrder r, String email) {
        Map<String, Boolean> res = new HashMap<>();
        res.put("isFail", false);
        res.put("isChangeCard", false);
        res.put("resStatus", false);
        //执行代扣
        String apiResult = "false";
        try {
            setPayTransResult(r, goPayTransAndSaveLog(r, email, apiResult), res);
        } catch (BusinessException e) {
            log.error("执行批扣借据号 {} 异常，code = {}，errMsg = {}, details = {}", r.getLoanSerialNo(), e.getCode(), e.getMessage(), e.getDetails());
            prossPayTranBusinessException(r, res, e);
            res.put("isFail", true);
        } finally {
            repaymentOrderService.updateRepaymentOrder(r);
        }
        return res;
    }

    /**
     * 设置请求状态
     * @param r
     * @param result
     * @param res
     */
    private void setPayTransResult(RepaymentOrder r, Map<String, String> result, Map<String, Boolean> res) {
        log.info("设置代扣状态，代扣结果 result = {}", result);

        String apiResult = result.get("apiResult");
        //更新扣款申请状态
        if ("success".equals(apiResult)) {
            //r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
            try {
                r.setPayTime(DateUtils.parseUTCText(result.get("payTime")));
            } catch (Exception e) {
                log.warn("paidAt format error", e.getMessage());
                r.setPayTime(new Date());
            }
            r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
            res.put("resStatus", true);
        } else if ("pending".equals(apiResult) || "timeout".equals(apiResult)) {
            r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
        } else {
            res.put("isFail", true);
            r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
        }
    }

    /**
     * 处理代扣异常
     *
     * @param r
     * @param res
     * @param e
     */
    private void prossPayTranBusinessException(RepaymentOrder r, Map<String, Boolean> res, BusinessException e) {
        if (RspEnmu.EXTERNAL_API_RSP_FALSE.getCode().equals(e.getCode())) {
            r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
            r.setRemark(e.getDetails());
        } else if (RspEnmu.WITHHOLD_CHANGE_CARD.getCode().equals(e.getCode())) {
            r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
            r.setRemark(e.getDetails());
            res.put("isChangeCard", true);
        } else {
            r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
        }
        res.put("isFail", true);
    }

    /**
     * @param r
     * @param email
     * @param apiResult
     * @return
     */
    private Map<String, String> goPayTransAndSaveLog(RepaymentOrder r, String email, String apiResult) {
        WithholdTrans withholdTrans = WithholdTrans.builder().withholdAmt(r.getRepaymentAmt())
                .authorizationCode(r.getBankCard()).email(email)
                .payTransSerialNo(r.getPayTransSerialNo()).build();
        JSONObject debitResult = payTransService.withholdTrans(withholdTrans);
        if (null != debitResult && null != debitResult.get("status") && StringUtils.isNotBlank(debitResult.getString("status"))) {
            apiResult = debitResult.getString("status");
        }
        if (null != debitResult && null != debitResult.get("gateway_response") && StringUtils.isNotBlank(debitResult.getString("gateway_response"))) {
            r.setRemark(debitResult.getString("gateway_response"));
        }
        //记录日志
        externalApiService.dealTransferData("charge." + apiResult, "interface", r.getCustNo(), debitResult);

        Map<String, String> result = new HashMap<>();
        result.put("apiResult", apiResult);
        result.put("payTime", getPayAt(debitResult));

        return result;
    }

    private static String getPayAt(JSONObject debitResult) {
        String payTime = StringUtils.getValue(debitResult, "paid_at");
        if (StringUtils.isBlank(payTime)) {
            payTime = StringUtils.getValue(debitResult, "transaction_date");
        }
        return payTime;
    }

    private static Date getPayTime(JSONObject debitResult) {
        try {
            return DateUtils.parseUTCText(getPayAt(debitResult));
        } catch (Exception e) {
            log.warn("paidAt format error", e.getMessage());
            return new Date();
        }
    }

    //代扣及冲账操作
    @Override
    public Map<String, Boolean> doingRepayment(RepaymentOrder r, String email, String cardNumber) {
        boolean isFail = false;
        boolean isChangeCard = false;

        Map<String, Boolean> map = new HashMap<>();

        //执行代扣
        String apiResult = "false";
        r.setPayTransSerialNo(IdUtils.reqId());
        repaymentOrderService.updateRepaymentOrder(r);
        try {
            WithholdTrans withholdTrans = WithholdTrans.builder().withholdAmt(r.getRepaymentAmt())
                    .authorizationCode(r.getBankCard()).email(email)
                    .payTransSerialNo(r.getPayTransSerialNo()).build();
            JSONObject debitResult = payTransService.withholdTrans(withholdTrans);
            if (null != debitResult && null != debitResult.get("status") && StringUtils.isNotBlank(debitResult.getString("status"))) {
                apiResult = debitResult.getString("status");
            }
            if (null != debitResult && null != debitResult.get("gateway_response") && StringUtils.isNotBlank(debitResult.getString("gateway_response"))) {
                r.setRemark(debitResult.getString("gateway_response"));
            }

            externalApiService.dealTransferData("charge." + apiResult, "interface", r.getCustNo(), debitResult);

            //更新扣款申请状态
            if ("success".equals(apiResult)) {
                r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS);
                r.setPayTime(getPayTime(debitResult));

                String redisKey = BizConstants.LOAN_REPAY_CARD + r.getLoanSerialNo();
                redisCache.deleteObject(redisKey);
            } else if ("pending".equals(apiResult) || "timeout".equals(apiResult)) {
                r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
            } else {
                isFail = true;
                r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
                String redisKey = BizConstants.LOAN_REPAY_CARD + r.getLoanSerialNo();
                redisCache.deleteObject(redisKey);
            }

            repaymentOrderService.updateRepaymentOrder(r);

            //如果repaymentOrder属于主动还款、催收，则发短信
            if (BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATE.equals(r.getActionType())
                    || BizConstants.LOAN_REPAYMENT_ORDER_TYPE_COLLECTION.equals(r.getActionType())) {
                //发送通知短信
                //this.sendRepaidResultSms(r);
                LoanIou loanIou = loanIouService.getLoanIouByLoanSerialNo(r.getLoanSerialNo());
                if (r.getRepayStatus().equals(BizConstants.REPAYMENT_ORDER_STATUS_FAIL)) {
                    iRepaymentSmsService.activeRepayment(r, loanIou, "fail");
                } else if (r.getRepayStatus().equals(BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS)) {
                    iRepaymentSmsService.activeRepayment(r, loanIou, "success");
                }
            }


            //如果代扣成功就冲账
            if (BizConstants.REPAYMENT_ORDER_STATUS_SUCCESS == r.getRepayStatus()) {
                // 判断是否有减免，有先进行减免冲账
                if (null != r.getCustReduceOrderId()) {
                    repaymentService.doReduceAmtStrike(r);
                }
                repaymentService.repaymentNoLock(r);
            }
        } catch (BusinessException e) {
            log.error("执行批扣借据号 {} 异常，code = {}，errMsg = {}, details = {}", r.getLoanSerialNo(), e.getCode(), e.getMessage(), e.getDetails());
            //网络异常 状态维持为还款中
//            if (e instanceof BusinessException && RspEnmu.NETWORK_BUSY.getRetCode().equals(e.getCode())) {
//                apiResult = "pending";
//            }
            if (RspEnmu.EXTERNAL_API_RSP_FALSE.getCode().equals(e.getCode())) {
                r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
                r.setRemark(e.getDetails());
                repaymentOrderService.updateRepaymentOrder(r);
                isFail = true;
            } else if (RspEnmu.WITHHOLD_CHANGE_CARD.getCode().equals(e.getCode())) {
                r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_FAIL);
                r.setRemark(e.getMessage());
                repaymentOrderService.updateRepaymentOrder(r);
                isFail = true;
                isChangeCard = true;
            } else {
                r.setRepayStatus(BizConstants.REPAYMENT_ORDER_STATUS_DOING);
                repaymentOrderService.updateRepaymentOrder(r);
                isFail = true;
            }
        }

        map.put("isFail", isFail);
        map.put("isChangeCard", isChangeCard);
        return map;
    }

    @Override
    public boolean doRepaymentAction(String serialNo, CustBankCard custBankCard, String reduceFlag) {
        String custNo = custBankCard.getCustNo();
        boolean result = false;
        //锁住客户账户再操作
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + custNo;
        RLock lock = redisCache.lock(lockKey);
        try {
            long start = System.currentTimeMillis();

            //获取借据详情
            LoanIou LoanIouQuery = new LoanIou();
            LoanIouQuery.setCustNo(custNo);
            LoanIouQuery.setLoanSerialNo(serialNo);
            LoanIouQuery.setState(UserConstants.BUSINESS_NORMAL);
            LoanIou loanIou = loanIouService.getLoanIouDetail(LoanIouQuery);

            //获取账户信息
            CreditAccount accountQuery = new CreditAccount();
            accountQuery.setCustNo(custNo);
            accountQuery.setOnLoan(BizConstants.ACCOUNT_ON_lOAN_STATUS_1);
            accountQuery.setState(UserConstants.BUSINESS_NORMAL);
            List<CreditAccount> selectCreditAccountList = creditAccountService.selectCreditAccountList(accountQuery);

            if (null != loanIou && selectCreditAccountList.size() == 1) {
                if (("hasReduce").equals(reduceFlag)) {
                    // 主动还款减免
                    BigDecimal reduceAmt = getQucikRepayReduceAmt(loanIou);
                    // 设置减免金额，用于createRepaymentOrder
                    loanIou.setReduceAmt(reduceAmt);
                }
                RepaymentOrder repaymentOrder = this.createRepaymentOrder(loanIou, custBankCard.getAuthorizationCode(), BizConstants.LOAN_REPAYMENT_ORDER_TYPE_OPERATE, null, "Active");
                if (null != repaymentOrder) {
                    if (("hasReduce").equals(reduceFlag)) {
                        // 主动还款减免,创建订单，更新RepaymentOrder custorderid
                        repaymentOrder = doQucikRepayReduce(loanIou, repaymentOrder);
                    }
                    //发起代扣
                    boolean isFail = this.doingRepayment(repaymentOrder, custBankCard.getEmail(), custBankCard.getCardNumber()).get("isFail");
                    if (!isFail) {
                        result = true;
                        //更新账户信息
                        this.updateAccount(selectCreditAccountList.get(0));
                    }
                }
            }
            //无需重试
            long end = System.currentTimeMillis();
            log.info("锁占用时间，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            log.error("doRepaymentAction线程sleep异常", e);
        } finally {
            lock.unlock();
        }

        return result;
    }

    /**
     * 主动还款含优惠信息
     *
     * @param repaymentOrder 1里面进行重写，repaymentorder创建好后
     *                       2创建减免订单，发起减免冲账，先付服务费再本金？要全部成功才能有状态 offset 状态是等待
     *                       3再发起卡金额扣款，成功-，发起冲账，更新2状态，更新全部状态
     *                       失败，webhook成功-发起冲账更新2状态，更新全部状态
     */
    private RepaymentOrder doQucikRepayReduce(LoanIou loanIou, RepaymentOrder repaymentOrder) {
        ReduceConfig reduceConfig = reduceConfigMapper.getStatucOn();
        BigDecimal reduceAmt = doCalCustReduce.checkCustReduce(loanIou.getLoanSerialNo()).getReduceAmont();
        if (reduceAmt.compareTo(BigDecimal.ZERO) == 1) {
            CustReduceOrder custReduceOrder = getCustReduceOrder(loanIou, repaymentOrder, reduceConfig, reduceAmt);
            custReduceOrder.setId(SnowflakeIdWorker.getIdWorkerInstance().nextId());// 设置id
            repaymentOrder.setCustReduceOrderId(custReduceOrder.getId());
            custReduceOrderService.insertAndupdateRorder(custReduceOrder);
        }
        repaymentOrder.setReduceFee(reduceAmt);// 设置减免金额
        return repaymentOrder;
    }

    private BigDecimal getQucikRepayReduceAmt(LoanIou loanIou) {
        BigDecimal reduceAmt = doCalCustReduce.checkCustReduce(loanIou.getLoanSerialNo()).getReduceAmont();
        return reduceAmt;
    }

    private CustReduceOrder getCustReduceOrder(LoanIou loanIou, RepaymentOrder repaymentOrder, ReduceConfig reduceConfig, BigDecimal reduceAmt) {
        CustReduceOrder custReduceOrder = new CustReduceOrder();
        custReduceOrder.setCustNo(loanIou.getCustNo());
        custReduceOrder.setRepaymentNo(repaymentOrder.getRepaymentNo());
        custReduceOrder.setActionType(ReduceConfig.ACTION_TYPE_QUICK_REPAY);

        custReduceOrder.setRepaymentAmtMin(reduceConfig.getRepaymentAmtMin());
        custReduceOrder.setNumLimit(reduceConfig.getNumLimit());
        custReduceOrder.setRate(reduceConfig.getRate());
        custReduceOrder.setDayLastAt(reduceConfig.getDayLastAt());
        custReduceOrder.setReduceAmtMin(reduceConfig.getReduceAmtMin());
        custReduceOrder.setReduceAmtMax(reduceConfig.getReduceAmtMax());

        custReduceOrder.setStatus(repaymentOrder.getRepayStatus());

        custReduceOrder.setServiceFee(loanIou.getServiceFee());
        custReduceOrder.setCurrentReduceAmt(reduceAmt);
        return custReduceOrder;
    }


    @Override
    public RepaymentOrder doCollectionRepaymentAction(LoanIou loan, CreditAccount account, CustBankCard custBankCard, BigDecimal repayAmt) {
        RepaymentOrder repaymentOrder = null;
        //锁住客户账户再操作
        String lockKey = LoanConstants.CUST_ACCOUNT_LOCK_KEY_PREFIX + account.getCustNo();
        RLock lock = redisCache.lock(lockKey);
        try {
            long start = System.currentTimeMillis();
            //发起代扣申请记录
            repaymentOrder = this.createRepaymentOrder(loan, custBankCard.getAuthorizationCode(),
                    BizConstants.LOAN_REPAYMENT_ORDER_TYPE_COLLECTION, repayAmt, null);
            if (null != repaymentOrder) {
                //发起代扣
                boolean isFail = this.doingRepayment(repaymentOrder, custBankCard.getEmail(), custBankCard.getCardNumber()).get("isFail");
                if (!isFail) {
                    //更新账户信息
                    this.updateAccount(account);
                }
            }
            //无需重试
            long end = System.currentTimeMillis();
            log.info("锁占用时间，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            log.error("doRepaymentAction线程sleep异常", e);
        } finally {
            lock.unlock();
        }
        return repaymentOrder;
    }

    @Override
    public List<LoanIou> getFirstByCustNo(String custNo) {
        return loanInfoMapper.getFirstByCustNo(custNo);
    }

    @Override
    public List<LoanIou> getFirstOverdueByCustNo(String custNo) {
        return loanInfoMapper.getFirstOverdueByCustNo(custNo);
    }

    @Override
    public BankCode getBanckInfoByAccountNumber(String accountNumber) {
        return loanInfoMapper.getBanckInfoByAccountNumber(accountNumber);
    }

    @Override
    public Integer getCurrentOverdue(String custNo) {
        return loanInfoMapper.getCurrentOverdue(custNo);
    }

    @Override
    public List<JSONObject> loanInfoListSort(List<JSONObject> loanInfoList) {

        List<JSONObject> newList = new ArrayList<>();
        List<JSONObject> outstanding = new ArrayList<>();
        List<JSONObject> overdue = new ArrayList<>();
        List<JSONObject> processings = new ArrayList<>();
        //repayDate排序
        List<JSONObject> repayDateSort = new ArrayList<>();
        List<Integer> pros = Arrays.asList(1, 3, 6, 7, 9);
        List<Integer> iouExclude = Arrays.asList(2, 1);
        loanInfoList.forEach(res -> {
            String loanStatus = Func.isEmpty(res.get("loanStatus")) ? "" : res.getString("loanStatus");
            String iouStatus = Func.isEmpty(res.get("iouStatus")) ? "" : res.getString("iouStatus");
            //Processing
            if (pros.contains(loanStatus) && !iouExclude.contains(iouStatus)) {
                processings.add(res);
                return;
            }
            //Overdue
            if ("2".equals(iouStatus)) {
                overdue.add(res);
                return;
            }
            //Outstanding
            if ("0".equals(iouStatus) && "4".equals(loanStatus)) {
                outstanding.add(res);
                return;
            }
            //其他默认按还款时间排序
            repayDateSort.add(res);
        });
        newList.addAll(processings);
        newList.addAll(overdue);
        newList.addAll(outstanding);
        newList.addAll(repayDateSort);
        return newList;
    }

    @Override
    public List<LoanIou> getBatchJobActiveLoanIouAll(String custNo, Integer minDays, Integer maxDays, Integer subtractDays, Integer surplusDays) {
        return loanInfoMapper.getBatchJobActiveLoanIouAll(custNo, minDays, maxDays, subtractDays, surplusDays);
    }

    @Override
    public boolean isLoanInfoListByInTransit(String custNo) {
        return loanInfoMapper.isLoanInfoListByInTransit(custNo) > 0;
    }
}